﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace System.Windows
{
	public static class ExtensionMethods
	{
		/// <summary>
		/// Gets the ratio between width and height (width/height).
		/// </summary>
		/// <param name="s"></param>
		/// <returns>width/height.</returns>
		public static double GetRatio(this Size s)
		{
			return s.Width / s.Height;
		}

		public static Size ChangeRatio(this Size s, double Ratio, ChangeRatioOptions opt)
		{
			double r = s.GetRatio();
			Size RetVal = s;

			if (opt == ChangeRatioOptions.IncreaseAreaSize)
			{
				if (r < Ratio)
					RetVal.Width = s.Height * Ratio;
				else
					RetVal.Height = s.Width / Ratio;
				return RetVal;
			}
			if (opt == ChangeRatioOptions.DecreaseAreaSize)
			{
				if (r > Ratio)
					RetVal.Width = s.Height * Ratio;
				else
					RetVal.Height = s.Width / Ratio;
				return RetVal;
			}

			// if (opt == ChangeRatioOptions.KeepAreaSize)
			double a = System.Math.Sqrt((s.Width * s.Height) / Ratio);
			RetVal.Height = a;
			RetVal.Width = a * Ratio;
			return RetVal;
		}

		public static Rect ChangeRatio(this Rect r, double Ratio, ChangeRatioOptions opt, RectOption anchor)
		{
			Size s = r.GetSize().ChangeRatio(Ratio, opt);
			if (anchor == RectOption.TopLeft)
				return s.ToRectangle(r.GetTopLeft(), anchor);
			if (anchor == RectOption.TopRight)
				return s.ToRectangle(r.GetTopRight(), anchor);
			if (anchor == RectOption.BottomLeft)
				return s.ToRectangle(r.GetBottomLeft(), anchor);
			if (anchor == RectOption.BottomRight)
				return s.ToRectangle(r.GetBottomRight(), anchor);
			if (anchor == RectOption.Center)
				return s.ToRectangle(r.GetCenter(), anchor);
			return new Rect(0, 0, 0, 0);
		}

		public enum ChangeRatioOptions : byte
		{
			/// <summary>
			/// Keeps the length of one side, lenghtens the other, thus increases the size of the area.
			/// </summary>
			IncreaseAreaSize,
			/// <summary>
			/// Keeps the length of one side, shortens the other, thus dencreases the size of the area.
			/// </summary>
			DecreaseAreaSize,
			/// <summary>
			/// Adjusts both sides. The size of the area stays the same.
			/// </summary>
			KeepAreaSize
		}

		public enum RectOption : byte
		{
			TopLeft,
			TopRight,
			BottomLeft,
			BottomRight,
			Center,
			None
		}

		public static Rect ToRectangle(this Size s, Point p, RectOption op)
		{
			if (op == RectOption.TopLeft)
				return new Rect(p, s);
			if (op == RectOption.TopRight)
				return new Rect(p.X - s.Width, p.Y, s.Width, s.Height);
			if (op == RectOption.BottomLeft)
				return new Rect(p.X, p.Y - s.Height, s.Width, s.Height);
			if (op == RectOption.BottomRight)
				return new Rect(p.X - s.Width, p.Y - s.Height, s.Width, s.Height);
			if (op == RectOption.Center)
				return new Rect(p.X - s.Width / 2, p.Y - s.Height / 2, s.Width, s.Height);
			return new Rect(0, 0, 0, 0);
		}

		public static Point GetCenter(this Rect r)
		{
			return new Point(r.Left + r.Width / 2, r.Top + r.Height / 2);
		}

		public static Point GetTopLeft(this Rect r)
		{
			return new Point(r.Left, r.Top);
		}

		public static Point GetTopRight(this Rect r)
		{
			return new Point(r.Right, r.Top);
		}

		public static Point GetBottomLeft(this Rect r)
		{
			return new Point(r.Left, r.Bottom);
		}

		public static Point GetBottomRight(this Rect r)
		{
			return new Point(r.Right, r.Bottom);
		}

		public static RectOption GetRectOption(this Rect r, Point p)
		{
			if (p == r.GetTopLeft())
				return RectOption.TopLeft;
			if (p == r.GetTopRight())
				return RectOption.TopRight;
			if (p == r.GetBottomLeft())
				return RectOption.BottomLeft;
			if (p == r.GetBottomRight())
				return RectOption.BottomRight;
			return RectOption.None;
		}

		public static Size GetSize(this Rect r)
		{
			return new Size(r.Width, r.Height);
		}

		public static double GetRatio(this Rect r)
		{
			return r.Width / r.Height;
		}

		public static System.Numerics.Complex ToComplex(this Point p)
		{
			return new Numerics.Complex(p.X, p.Y);
		}

		/// <summary>
		/// Renders the <see cref="UIElement"/> to a <see cref="WriteableBitmap"/>. The UIElement is not
		/// required to be part of the VisualTree.
		/// </summary>
		public static WriteableBitmap RenderToImage(this UIElement uiElement)
		{
			uiElement.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
			uiElement.Arrange(new Rect(new Point(0, 0), uiElement.DesiredSize));
			return new WriteableBitmap(uiElement, null);
		}
	}
}
